﻿using Engine;
using Engine.Graphics;
using Game;
using GameEntitySystem;
using SAGhoul.Blocks;
using SAGhoul.ParticleSystem;
using System.Linq;
using TemplatesDatabase;

namespace SAGhoul
{
	public class ComponentAquagaze : Component, IUpdateable
	{
		public const int MaxLevel = 4;//最大等级
		public const float FeedsPerLevel = 100;
		public const float FeedConversionRatio = 0.5f;//转化比，即喂食1个食物能多产出多少肉
		public static float[] FeedsPerLevels => [20f, 80f, 120f, 180f, 50f];//第0个元素是0到1级升级喂食花费，后面以此类推。最后一个是4级到产蛋的喂食花费
		public static float[] DaysPerLevels => [2f, 3f, 4f, 5f, 1f];//第0个元素是0到1级升级天数花费，后面以此类推。最后一个是4级到产蛋的天数花费

		public static float[] StageScales => [0.5f, 1.2f, 2f, 3.6f, 5f];//每个生长阶段的体型缩放
		public static float SumFeeds(int level)
		{
			if (level <= 0)
				return 0f;
			float result = 0f;
			for (int i = 0; i < level; i++)
				result += FeedsPerLevels[i];
			return result;
		}
		public SubsystemPickables SubsystemPickables
		{
			get; set;
		}
		public SubsystemParticles SubsystemParticles
		{
			get; set;
		}
		public SubsystemAudio SubsystemAudio
		{
			get; set;
		}
		public ComponentCreature ComponentCreature
		{
			get; set;
		}
		public ComponentFishModel ComponentFishModel
		{
			get; set;
		}
		public ComponentEatPickableBehavior ComponentEatPickableBehavior
		{
			get; set;
		}
		public bool SoulCondensed
		{
			get; set;
		}
		public int GrowthLevel => growthLevel;
		public float GrowthProgress => growthProgress;
		public float GrowthRate => growthProgress / FeedsPerLevels[GrowthLevel];// (growthProgress - FeedsPerLevel * MathUtils.Floor(GrowthLevel)) / FeedsPerLevel;
		public float FeedRate => MathUtils.Saturate(feedCount / FeedsPerLevels[GrowthLevel]);
		public UpdateOrder UpdateOrder => UpdateOrder.Default;

		readonly Game.Random m_random = new();
		EliteParticleSystem m_eliteParticleSystem;
		readonly Texture2D[] m_textures = new Texture2D[MaxLevel + 1];
		float growthProgress, feedCount, nourishedTime;
		internal int growthLevel;
		EggBlock.EggType m_eggType;
		Vector3 m_boxSize;
		float m_mass, m_attackResilience, m_swimSpeed;
		float stuckTime;
		public override void Load(ValuesDictionary valuesDictionary, IdToEntityMap idToEntityMap)
		{
			base.Load(valuesDictionary, idToEntityMap);
			ValuesDictionary valuesDictionary1 = DatabaseManager.FindEntityValuesDictionary(Entity.ValuesDictionary.DatabaseObject.Name, throwIfNotFound: true);
			m_attackResilience = valuesDictionary1.GetValue<ValuesDictionary>("Health").GetValue<float>("AttackResilience");
			m_swimSpeed = valuesDictionary1.GetValue<ValuesDictionary>("Locomotion").GetValue<float>("SwimSpeed");
			ValuesDictionary valuesDictionary_Body = valuesDictionary1.GetValue<ValuesDictionary>("Body");
			m_boxSize = valuesDictionary_Body.GetValue<Vector3>("BoxSize");
			m_mass = valuesDictionary_Body.GetValue<float>("Mass");
			SubsystemPickables = Project.FindSubsystem<SubsystemPickables>(throwOnError: true);
			SubsystemAudio = Project.FindSubsystem<SubsystemAudio>(throwOnError: true);
			SubsystemParticles = Project.FindSubsystem<SubsystemParticles>(throwOnError: true);
			ComponentCreature = Entity.FindComponent<ComponentCreature>(throwOnError: true);
			ComponentFishModel = Entity.FindComponent<ComponentFishModel>(throwOnError: true);
			ComponentEatPickableBehavior = Entity.FindComponent<ComponentEatPickableBehavior>(throwOnError: true);
			SoulCondensed = valuesDictionary.GetValue("SoulCondensed", false);
			growthLevel = valuesDictionary.GetValue("GrowthLevel", 0);
			growthProgress = valuesDictionary.GetValue("GrowthProgress", 0f);
			feedCount = valuesDictionary.GetValue("FeedCount", 0f);
			nourishedTime = valuesDictionary.GetValue("NourishedTime", 0f);
			m_eggType = ((EggBlock)BlocksManager.Blocks[EggBlock.Index]).GetEggTypeByCreatureTemplateName(Entity.ValuesDictionary.DatabaseObject.Name);
			for (int i = 0; i < m_textures.Length; i++)
			{
				m_textures[i] = ContentManager.Get<Texture2D>("Textures/Aquagaze/" + i.ToString());
			}
			ComponentEatPickableBehavior.m_stateMachine.m_states.Remove("Inactive");
			ComponentEatPickableBehavior.m_stateMachine.m_states.Remove("Move");
			ComponentEatPickableBehavior.m_stateMachine.m_states.Remove("Eat");
			ComponentEatPickableBehavior.m_stateMachine.AddState("Inactive",
				delegate
				{
					ComponentEatPickableBehavior.m_importanceLevel = 0f;
					ComponentEatPickableBehavior.m_pickable = null;
				},
				StateMachine_Inactive_Update,
				delegate
				{
					stuckTime = 0f;
				}
			);
			ComponentEatPickableBehavior.m_stateMachine.AddState("Move",
				StateMachine_Move_Enter,
				StateMachine_Move_Update,
				null
			);
			ComponentEatPickableBehavior.m_stateMachine.AddState("Eat",
				delegate
				{
					ComponentEatPickableBehavior.m_eatTime = m_random.Float(4f, 5f);
					ComponentEatPickableBehavior.m_blockedTime = 0f;
				},
				StateMachine_Eat_Update,
				null
			);

		}
		public override void Save(ValuesDictionary valuesDictionary, EntityToIdMap entityToIdMap)
		{
			base.Save(valuesDictionary, entityToIdMap);
			valuesDictionary.SetValue("SoulCondensed", SoulCondensed);
			valuesDictionary.SetValue("GrowthLevel", growthLevel);
			valuesDictionary.SetValue("GrowthProgress", growthProgress);
			valuesDictionary.SetValue("FeedCount", feedCount);
			valuesDictionary.SetValue("NourishedTime", nourishedTime);
		}
		public override void OnEntityRemoved()
		{
			if (m_eliteParticleSystem != null)
			{
				m_eliteParticleSystem.IsStopped = true;
				m_eliteParticleSystem = null;
			}
		}
		public void Feed(int count) => feedCount += count;
		public void Nourish(float time) => nourishedTime += time;
		public void LayEgg(int count)
		{
			int value = Terrain.MakeBlockValue(EggBlock.Index, 0, EggBlock.SetIsLaid(EggBlock.SetEggType(0, m_eggType.EggTypeIndex), false));
			Matrix matrix = ComponentCreature.ComponentBody.Matrix;
			Vector3 position = 0.5f * (ComponentCreature.ComponentBody.BoundingBox.Min + ComponentCreature.ComponentBody.BoundingBox.Max);
			Vector3 value2 = 3f * Vector3.Normalize(-matrix.Forward + 0.1f * matrix.Up + 0.2f * m_random.Float(-1f, 1f) * matrix.Right);
			SubsystemPickables.AddPickable(value, count, position, value2, null, Entity);
			SubsystemAudio.PlaySound("Audio/EggLaid", 1f, m_random.Float(-0.1f, 0.1f), position, 2f, true);
		}
		public void Update(float dt)
		{
			nourishedTime = MathUtils.Max(0f, nourishedTime - dt);
			if (nourishedTime > 0f && ComponentCreature.ComponentHealth.Health > 0f)
			{
				BoundingBox boundingBox = ComponentCreature.ComponentBody.BoundingBox;
				Vector3 particlePosition = 0.5f * (boundingBox.Min + boundingBox.Max);
				float particleRadius = 0.5f;
				if (m_eliteParticleSystem == null)
				{//boss粒子
					m_eliteParticleSystem = new EliteParticleSystem();
					SubsystemParticles.AddParticleSystem(m_eliteParticleSystem);
				}
				m_eliteParticleSystem.Position = particlePosition;
				m_eliteParticleSystem.Color = new Color(155, 0, 55);
				m_eliteParticleSystem.Radius = particleRadius * MathUtils.Min(boundingBox.Max.X - boundingBox.Min.X, boundingBox.Max.Z - boundingBox.Min.Z);
			}
			else
			{
				if (m_eliteParticleSystem != null)
				{
					m_eliteParticleSystem.IsStopped = true;
					m_eliteParticleSystem = null;
				}
			}
			if (ComponentCreature.ComponentHealth.Health > 0f)
			{
				ComponentEatPickableBehavior.m_nextFindPickableTime = 0f;
				ComponentEatPickableBehavior.m_blockedCount = 0;
				ComponentEatPickableBehavior.m_satiation = 0f;
				ComponentEatPickableBehavior.m_eatTime = MathUtils.Min(float.Epsilon, ComponentEatPickableBehavior.m_eatTime);
				if (feedCount > 0f)
				{
					float s = FeedsPerLevels[GrowthLevel] / (DaysPerLevels[GrowthLevel] * 1200f);
					s *= nourishedTime > 0f ? 3f : 1f;//(加速后3倍速)
					float m = MathUtils.Min(feedCount, dt * s);
					feedCount -= m;
					growthProgress += m;
				}
				if (growthProgress >= FeedsPerLevels[GrowthLevel])
				{
					if (GrowthLevel >= MaxLevel)
						LayEgg(1);
					else
						growthLevel++;
					growthProgress = 0f;
				}
			}
			int stage = MathUtils.Clamp((int)MathUtils.Floor(GrowthLevel), 0, MaxLevel);
			float scale = StageScales[stage];
			ComponentFishModel.ModelScale = scale;
			ComponentCreature.ComponentHealth.AttackResilience = m_attackResilience * (1f + 0.5f * (scale - 1f));
			ComponentCreature.ComponentLocomotion.SwimSpeed = m_swimSpeed / (1f + 0.1f * (scale - 1f));
			ComponentCreature.ComponentBody.BoxSize = m_boxSize * scale;
			ComponentCreature.ComponentBody.Mass = m_mass * scale;
			ComponentFishModel.TextureOverride = m_textures[stage];
		}

		Pickable FindPickable(Vector3 position)
		{
			if (ComponentEatPickableBehavior.m_subsystemTime.GameTime > ComponentEatPickableBehavior.m_nextPickablesUpdateTime)
			{
				ComponentEatPickableBehavior.m_nextPickablesUpdateTime = ComponentEatPickableBehavior.m_subsystemTime.GameTime + m_random.Float(2f, 4f);
				ComponentEatPickableBehavior.m_pickables.Clear();
				foreach (Pickable pickable in ComponentEatPickableBehavior.m_subsystemPickables.Pickables)
					ComponentEatPickableBehavior.TryAddPickable(pickable);
				if (ComponentEatPickableBehavior.m_pickable != null && !ComponentEatPickableBehavior.m_pickables.ContainsKey(ComponentEatPickableBehavior.m_pickable))
					ComponentEatPickableBehavior.m_pickable = null;
			}
			float minDistanceSqr = float.MaxValue;
			Pickable result = null;
			foreach (Pickable key in ComponentEatPickableBehavior.m_pickables.Keys)
			{
				float num = Vector3.DistanceSquared(position, key.Position);
				if (num < minDistanceSqr)
				{
					minDistanceSqr = num;
					result = key;
				}
			}
			return result;
		}

		void StateMachine_Inactive_Update()
		{
			if (ComponentEatPickableBehavior.m_satiation < 1f)
			{
				if (ComponentEatPickableBehavior.m_pickable == null)
				{
					if (ComponentEatPickableBehavior.m_subsystemTime.GameTime > ComponentEatPickableBehavior.m_nextFindPickableTime)
					{
						ComponentEatPickableBehavior.m_nextFindPickableTime = ComponentEatPickableBehavior.m_subsystemTime.GameTime + m_random.Float(2f, 4f);
						ComponentEatPickableBehavior.m_pickable = FindPickable(ComponentEatPickableBehavior.m_componentCreature.ComponentBody.Position);
					}
				}
				else
					ComponentEatPickableBehavior.m_importanceLevel = m_random.Float(5f, 10f);
			}
			if (ComponentEatPickableBehavior.IsActive)
			{
				ComponentEatPickableBehavior.m_stateMachine.TransitionTo("Move");
				ComponentEatPickableBehavior.m_blockedCount = 0;
			}
		}
		void StateMachine_Move_Enter()
		{
			if (ComponentEatPickableBehavior.m_pickable != null)
			{
				float speed = ComponentEatPickableBehavior.m_satiation == 0f ? m_random.Float(0.5f, 0.7f) : 0.5f;
				int maxPathfindingPositions = ComponentEatPickableBehavior.m_satiation == 0f ? 1000 : 500;
				float num2 = Vector3.Distance(
					ComponentEatPickableBehavior.m_componentCreature.ComponentCreatureModel.EyePosition,
					ComponentEatPickableBehavior.m_componentCreature.ComponentBody.Position
				);
				ComponentEatPickableBehavior.m_componentPathfinding.SetDestination(
					ComponentEatPickableBehavior.m_pickable.Position,
					speed,
					1f + num2,
					maxPathfindingPositions,
					true,
					false,
					true,
					null
				);
				if (m_random.Float(0f, 1f) < 0.66f)
					ComponentEatPickableBehavior.m_componentCreature.ComponentCreatureSounds.PlayIdleSound(true);
			}
		}
		void StateMachine_Move_Update()
		{
			if (!ComponentEatPickableBehavior.IsActive)
				ComponentEatPickableBehavior.m_stateMachine.TransitionTo("Inactive");
			else if (ComponentEatPickableBehavior.m_pickable == null)
				ComponentEatPickableBehavior.m_importanceLevel = 0f;
			else if (ComponentEatPickableBehavior.m_componentPathfinding.IsStuck)
			{
				ComponentEatPickableBehavior.m_importanceLevel = 0f;
				ComponentEatPickableBehavior.m_satiation += 0.75f;
			}
			else if (!ComponentEatPickableBehavior.m_componentPathfinding.Destination.HasValue)
				ComponentEatPickableBehavior.m_stateMachine.TransitionTo("Eat");
			else if (Vector3.DistanceSquared(ComponentEatPickableBehavior.m_componentPathfinding.Destination.Value, ComponentEatPickableBehavior.m_pickable.Position) > 0.0625f)
				ComponentEatPickableBehavior.m_stateMachine.TransitionTo("PickableMoved");
			if (m_random.Float(0f, 1f) < 0.1f * ComponentEatPickableBehavior.m_subsystemTime.GameTimeDelta)
				ComponentEatPickableBehavior.m_componentCreature.ComponentCreatureSounds.PlayIdleSound(true);
			if (ComponentEatPickableBehavior.m_pickable != null)
			{
				ComponentEatPickableBehavior.m_componentCreature.ComponentCreatureModel.LookAtOrder = ComponentEatPickableBehavior.m_pickable.Position;
				float dSqr = Vector3.DistanceSquared(ComponentCreature.ComponentBody.Position, ComponentEatPickableBehavior.m_pickable.Position);
				if (ComponentEatPickableBehavior.m_pickables.Any((p) => Vector3.DistanceSquared(ComponentCreature.ComponentBody.Position, p.Key.Position) < dSqr))
				{
					stuckTime += Time.FrameDuration;
				}
				if (stuckTime >= 5f)
				{
					ComponentEatPickableBehavior.m_stateMachine.TransitionTo("Inactive");
					ComponentEatPickableBehavior.m_pickable = null;
				}
			}
			else
				ComponentEatPickableBehavior.m_componentCreature.ComponentCreatureModel.LookRandomOrder = true;
		}
		void StateMachine_Eat_Update()
		{
			if (!ComponentEatPickableBehavior.IsActive)
				ComponentEatPickableBehavior.m_stateMachine.TransitionTo("Inactive");
			if (ComponentEatPickableBehavior.m_pickable == null)
				ComponentEatPickableBehavior.m_importanceLevel = 0f;
			if (ComponentEatPickableBehavior.m_pickable != null)
			{
				if (Vector3.DistanceSquared(ComponentCreature.ComponentBody.Position, ComponentEatPickableBehavior.m_pickable.Position)
					< 0.640000045f)
				{
					ComponentEatPickableBehavior.m_eatTime -= ComponentEatPickableBehavior.m_subsystemTime.GameTimeDelta;
					ComponentEatPickableBehavior.m_blockedTime = 0f;
					if (ComponentEatPickableBehavior.m_eatTime <= 0.0)
					{
						ComponentEatPickableBehavior.m_satiation += 1f;
						int id = Terrain.ExtractContents(ComponentEatPickableBehavior.m_pickable.Value);
						if (id == typeof(SoulCondenseBlock).BlockIndex())
						{//食用灵魂凝固，立即死亡
							SoulCondensed = true;
							ComponentCreature.ComponentHealth.Injure(1f, null, false, LanguageControl.Get(typeof(SubsystemSAGArtifactBlockBehavior).Name, "SoulExplosion"));
						}
						else
						{
							int count = ComponentEatPickableBehavior.m_pickable.Count;
							Feed(count);
							if (id == typeof(SoulNourishmentBlock).BlockIndex())
								Nourish(1200f * count);
						}
						ComponentEatPickableBehavior.m_pickable.ToRemove = true;
						ComponentEatPickableBehavior.m_importanceLevel = 0f;
					}
				}
				else
				{
					float num = Vector3.Distance(
						ComponentEatPickableBehavior.m_componentCreature.ComponentCreatureModel.EyePosition,
						ComponentEatPickableBehavior.m_componentCreature.ComponentBody.Position
					);
					ComponentEatPickableBehavior.m_componentPathfinding.SetDestination(
						ComponentEatPickableBehavior.m_pickable.Position,
						0.3f,
						0.5f + num,
						0,
						false,
						true,
						false,
						null
					);
					ComponentEatPickableBehavior.m_blockedTime += ComponentEatPickableBehavior.m_subsystemTime.GameTimeDelta;
				}
				if (ComponentEatPickableBehavior.m_blockedTime > 3f)
				{
					ComponentEatPickableBehavior.m_blockedCount++;
					if (ComponentEatPickableBehavior.m_blockedCount >= 3)
					{
						ComponentEatPickableBehavior.m_importanceLevel = 0f;
						ComponentEatPickableBehavior.m_satiation += 0.75f;
					}
					else
						ComponentEatPickableBehavior.m_stateMachine.TransitionTo("Move");
				}
			}
			ComponentEatPickableBehavior.m_componentCreature.ComponentCreatureModel.FeedOrder = true;
			if (m_random.Float(0f, 1f) < 0.1f * ComponentEatPickableBehavior.m_subsystemTime.GameTimeDelta)
				ComponentEatPickableBehavior.m_componentCreature.ComponentCreatureSounds.PlayIdleSound(true);
			if (m_random.Float(0f, 1f) < 1.5f * ComponentEatPickableBehavior.m_subsystemTime.GameTimeDelta)
				ComponentEatPickableBehavior.m_componentCreature.ComponentCreatureSounds.PlayFootstepSound(2f);
		}

	}
}
